﻿using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using Cubicle.Core.Config;
using Cubicle.Core.Interfaces;
using Cubicle.Core.Shared;
using NLog;

namespace Cubicle.Core.Processors
{

    public class ProcessorQueue<T> : Processor<T>, IProcessorQueue
    {

        private static Logger _nlog = LogManager.GetCurrentClassLogger();

        protected List<IProcessor<T>> Queue = new List<IProcessor<T>>();

        public override T Process(T input)
        {
            return ProcessQueue(input);
        }

        public virtual T ProcessQueue(T input)
        {
            return Queue.Aggregate(input, (current, p) => p.Process(current));
        }

        public void Build(CompositionContainer container, ProcessorConfig config)
        {
            Config = config;

            // Construct processor pipeline
            if (config == null)
                return;
            _nlog.Trace("Building processor queue.".Format());
            Queue.Clear();

            var proc = ContainerServices.Create<T>(container, config);
            AddProcessor(proc);
        }

        public void AddProcessor(CompositionContainer container, ProcessorConfig config)
        {
            var processor = ContainerServices.Create<T>(container, config);
            processor.Config = config;
            processor.Parent = this;
            AddProcessor(processor);
        }

        private void AddProcessor(IProcessor<T> processor)
        {
            Queue.Add(processor);
        }

    }

    public abstract class ProcessorQueue<TIn, TOut> : Processor<TIn>, IProcessorQueue, IProcessor<TOut>
    {
        protected List<IProcessor<TOut>> Queue = new List<IProcessor<TOut>>();

        protected abstract TOut ConvertTo(TIn input);
        protected abstract TIn ConvertFrom(TOut input);

        public override TIn Process(TIn input)
        {
            return ConvertFrom(ProcessQueue(ConvertTo(input)));
        }

        public virtual TOut ProcessQueue(TOut input)
        {
            return Queue.Aggregate(input, (current, p) => p.Process(current));
        }

        public void AddProcessor(CompositionContainer container, ProcessorConfig config)
        {
            var processor = ContainerServices.Create<TOut>(container, config);
            processor.Config = config;
            processor.Parent = this;
            AddProcessor(processor);
        }

        private void AddProcessor(IProcessor<TOut> processor)
        {
            Queue.Add(processor);
        }

        public TOut Process(TOut input)
        {
            return ProcessQueue(input);
        }
    }

}